package com.koala.swb.i18n.resources;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.Locale;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import com.koala.excepcion.Excepcion;
import com.koala.excepcion.excepciones.Fantasma_excepcion;
import com.koala.excepcion.excepciones.Lectura_excepcion;

/**
 * 
 * @author cerokool32
 * @category Lenguage
 */
public class Diccionario implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * 
	 */
	//private static final long serialVersionUID = 6531164373448765365L;

	private Locale region;
	private ResourceBundle diccionario;
	private String app;
	private String tomo;
	
	private Logger log;
	private String logid;
	
	private Excepcion err;
	
	//path donde se encuentran los .properties agrupados por carpetas del mismo nombre de la aplicación.
	private static final String path="/koala/archivos/i18n/";
	
	/**
	 * Constructor orientado a la generación de nuevos tomos para una aplicación.
	 * @param app Nombre de la aplicación.
	 * @param tomo Tomo del diccionario.
	 */
	public Diccionario(String app, String tomo) {
		
		this.app=app;
		this.tomo=tomo;
		
		this.log=Logger.getLogger("Diccionario");
		
	}
	
	/**
	 * Constructor que inicializa el diccionario basado en la region que se le ingresa.
	 * Este constructor solo debera ser utilizado si existe el diccionario, de lo contrario
	 * utilice el constructor alternativo.
	 * @param region Region que se especifica para el lenguage.
	 * @param app Aplicación que solicita
	 * @param tomo Tomo del diccionario.
	 */
	public Diccionario(Locale region, String app, String tomo) throws Excepcion{
		
		this.region=region;
		this.app=app;
		this.tomo=tomo;
		
		RecargarDiccionario();
		
	}
	
	/**
	 * Obtiene una definición desde el diccionario.
	 * @param identificador Identificador de la definición.
	 * @return Definición.
	 */
	public String GetDefinicion(String identificador){
		
		return diccionario.getString(identificador);
		
	}
	
	/**
	 * Ingresa una nueva definición al diccionario.
	 * @param identificador Nuevo identificador de la definicion.
	 * @param definicion Nueva definición.
	 */
	public void SetDefinicion(String identificador, String definicion) throws Excepcion{
		
		IngresarDefinicion(identificador, definicion);	
		RecargarDiccionario();
		
	}
	
	/**
	 * Crea un nuevo diccionario de acuerdo a la region definida y un mapa inicial de definiciones.
	 * @param region Region definida para el diccionario.
	 * @param definiciones Mapa de definiciones iniciales para el diccionario.
	 */
	public void CrearDiccionario(Locale region, Map<String, String> definiciones) throws Excepcion{
		
		logid="Diccionario.CrearDiccionario: ";
		
		log.info(logid+"Inicia la creacion de un nuevo diccionario para la aplicacion "+app+" Tomo: "+tomo+" para la region: "+region.toString());
		
		this.region=region;
		
		CrearArchivo();
		
		log.info(logid+"Inicia ingreso del mapa de definiciones.");
		SetDefiniciones(definiciones);
		
		log.info(logid+"Diccionario Creado.");
		
	}
	
	/**
	 * Ingresa un mapa de definiciones al diccionario.
	 * @param Definiciones Mapa de definiciones.
	 */
	private void SetDefiniciones(Map<String, String> Definiciones) throws Excepcion{
		
		logid="Diccionario.SetDefiniciones: ";
		

		for(String id:Definiciones.keySet()){
			IngresarDefinicion(id, Definiciones.get(id));
		}
		
		RecargarDiccionario();

	}
	
	/**
	 * Localiza un archivo en el path del sistema y lo convierte en un objeto File nativo.
	 * @return Archivo en formato File nativo.
	 */
	private File LocalizarArchivo() throws Excepcion{
		
		File file = new File(Diccionario.path+app+"/"+app+"_"+tomo+"_"+region.toString()+".properties"); 
		
		String[] lang;
		
		//formato lang_country
		if(file.exists())
		
			return file;
		
		else{
			
			lang=region.toString().split("_");
			file = new File(Diccionario.path+app+"/"+app+"_"+tomo+"_"+lang[0]+".properties");
			
			//formato lang
			if(file.exists())
			
				return file;
			
			else{
				
				//sin formato de region
				file = new File(Diccionario.path+app+"/"+app+"_"+tomo+".properties");
		
				if(file.exists())
					
					return file;
				
				else{
					
					err=new Fantasma_excepcion("Archivo no encontrado.", null, null);
					err.AdicionarParametro("Path1", Diccionario.path+app+"/"+app+"_"+tomo+"_"+region.toString()+".properties");
					err.AdicionarParametro("Path2", Diccionario.path+app+"/"+app+"_"+tomo+"_"+lang[0]+".properties");
					err.AdicionarParametro("Path3", Diccionario.path+app+"/"+app+"_"+tomo+".properties");
					throw err;
					
				}
			}
		}
	}
	
	/**
	 * Crea un nuevo archivo en el path del sistema en el cual se almacenan los archivos I18n.
	 * En caso de tratarse de archivos para una nueva aplicacion, se genera una estructura de directorios con el nombre de la nueva
	 * aplicación.
	 */
	private void CrearArchivo()throws Excepcion{
		
		logid="Diccionario.CrearArchivo: ";
		
		File dir=new File(Diccionario.path+app);
		File file= new File(Diccionario.path+app+"/"+app+"_"+tomo+"_"+region.toString()+".properties");
				
		try {
			
			if(!dir.exists()){
				
				if(!dir.mkdirs()){
					
					err=new Fantasma_excepcion("El directorio "+dir.getAbsolutePath()+" no pudo ser creado.",null, null);
					throw err;
				}
			}
						
			if(!file.exists()){
			
				if(!file.createNewFile()){
					
					err=new Fantasma_excepcion("El archivo "+file.getAbsolutePath()+" no pudo ser creado.", null, null);
					throw err;
					
				}
			}
			
		} catch (Exception e) {
			
			err=new Fantasma_excepcion("Ocurrio un error durante la creacion del Directorio/Archivo.", e.getStackTrace(), null);
			throw err;
		}
		
	}
	
	/**
	 * Ingresa una nueva definicion al diccionario.
	 * @param id Identificador de la definición.
	 * @param valor Nueva definición.
	 */
	private void IngresarDefinicion(String id, String valor) throws Excepcion{
		
		logid="Diccionario.IngresarDefinicion: ";
		
		String archivo=Diccionario.path+app+"/"+app+"_"+tomo+"_"+region.toString()+".properties";
		File file=new File(archivo);
		
		try {
			
			if(file.exists()){
				
				BufferedWriter Fescribe=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true), "utf-8"));
				
				Fescribe.write(id+"="+valor+"\r\n");
				
				Fescribe.close();
				
			}
			else{
				log.info(logid+"Archivo no encontrado, se desplegara una excepcion Fantasma");
				err=new Fantasma_excepcion("El archivo "+file.getAbsolutePath()+" No se encuentra o no existe.", null, null);
				throw err;
			}
 
		} catch (IOException e) {
			
			err=new Lectura_excepcion("Error al escribir en el archivo "+file.getAbsolutePath(), e.getStackTrace(), null);
			err.AdicionarParametro("id", id);
			err.AdicionarParametro("valor", valor);
			throw err;
			
		}
		
	}
	
	/**
	 * Carga en memoria el resourceBundle reemplazando el existente. Util para utilizar la ultima version del diccionario.
	 */
	private void RecargarDiccionario() throws Excepcion{
		
		File file =LocalizarArchivo();
		
	    InputStream stream = null;
	    
	    try {
	    	
	    	stream = new FileInputStream(file);
			diccionario = new PropertyResourceBundle(stream);
			
		} catch (FileNotFoundException e) {
			
			err=new Fantasma_excepcion("Archivo "+file.getAbsolutePath()+" no encontrado", e.getStackTrace(), null);
			throw err;
			
		} catch (IOException e) {
			
			err=new Lectura_excepcion("Error subiendo el diccionario a memoria.", e.getStackTrace(), null);
			throw err;
						
		}
	}

	/**
	 * Valida si existe una definición en el diccionario.
	 * @param id Identificador de la definicion.
	 * @return TRUE si existe la definicion.
	 */
	@SuppressWarnings("unused")
	private boolean ExisteID(String id) throws Excepcion{
		
		File file = LocalizarArchivo();
		
		try {  
		         
	           BufferedReader Flee= new BufferedReader(new FileReader(file));  
	           
	           String Slinea;  
	           String[] idLinea;
	             
	           while((Slinea=Flee.readLine())!=null) {  
	        	   idLinea=Slinea.split("=");
	        	   if(idLinea[0].equals(id))
	        		   return true;                
	           }  
	           
	           Flee.close();  
		       
		   } catch (Exception ex) {  
		       	//lanzar excepcion de lectura   
			   err=new Lectura_excepcion("Error localizando el id:"+id, ex.getStackTrace(), null);
			   err.AdicionarParametro("id", id);
			   throw err;
			   
		   }  
		return false;
	}
	
	/**
	 * Reemplaza una definicion identificada con el di ingresado, reemplazandolo con el nuevo valor ingresado.
	 * @param id Identificador de la definición.
	 * @param nuevoValor Nueva definicion para el identificador.
	 */
	@SuppressWarnings("unused")
	private void ReemplazarValor(String id, String nuevoValor) throws Excepcion{
		
		 Random numaleatorio= new Random(3816L);
		 
		 File file=LocalizarArchivo();
		 
		 String nuevoFile=file.getParent()+"/auxiliar"+String.valueOf(Math.abs(numaleatorio.nextInt()))+".txt";
		 
		 File NuevoFile=new File(nuevoFile);
		 
		 try {
			 
			 BufferedReader Flee= new BufferedReader(new FileReader(file));
			 
			 String Slinea;
			 String[] auxID;
			 
			 while((Slinea=Flee.readLine())!=null) { 
				 
				 auxID=Slinea.split("=");
				 
				 if(auxID[0].equals(id))
					 EscribirEnArchivo(NuevoFile, auxID+"="+nuevoValor);
				else
					EscribirEnArchivo(NuevoFile, Slinea);
			 }
			 
			 BorrarArchivo(file);
			 NuevoFile.renameTo(file);
			 Flee.close();
			 
		} catch (Exception e) {

			err=new Lectura_excepcion("Error Reemplazado el valor del identificador "+id, e.getStackTrace(), null);
			err.AdicionarParametro("id", id);
			err.AdicionarParametro("nuevoValor", nuevoValor);
			
			throw err;
		}
		
	}
	
	/**
	 * Borra un diccionario del path del sistema.
	 * @param file Archivo en formato File nativo.
	 */
	private void BorrarArchivo(File file) throws Excepcion {  
	     
		try {  
	    	 
	         if(file.exists()){  
	         
	        	 file.delete();   
	        	 
	         }
	         
	     } catch (Exception ex) {  

	    	 err=new Fantasma_excepcion("Error al eliminar el archivo "+file.getAbsolutePath(), ex.getStackTrace(), null);
	    	 err.AdicionarParametro("file", file.getAbsolutePath());
	    	 
	    	 throw err;
	         
	     }  
	}   

	/**
	 * Ingresa una nueva linea en el Archivo en formato File nativo.
	 * @param file Archivo en formato File nativo.
	 * @param SCadena Nueva linea del archivo.
	 */
    private void EscribirEnArchivo(File file,String SCadena) throws Excepcion{  
        
    	try {  
                 
                BufferedWriter Fescribe=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true), "utf-8"));  
                
                Fescribe.write(SCadena + "\r\n");  
                  
                Fescribe.close();
                
             } catch (Exception ex) {
            	 
            	 err=new Lectura_excepcion("Error al escribir en archivo "+file.getAbsolutePath(), ex.getStackTrace(), null);
            	 err.AdicionarParametro("file", file.getAbsolutePath());
            	 err.AdicionarParametro("SCadena", SCadena);
            	 
            	 throw err;
            	   
             }   
      }  

}
